package services;

import gestures.Gesture;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;

import services.subscribers.IGestureListener;

/**
 * Data structure class to hold a registration object to listen for a specific
 * gesture in a bounded area
 * 
 * @author PDAY (The MITRE Corporation)
 * 
 */
public class ListenerRegistration {

	private String listenerName;
	private Rectangle2D.Float captureBounds;
	private IGestureListener listenerGesture;
	private boolean genericListener;

	public ListenerRegistration(String listenerName,
			Rectangle2D.Float captureBounds, IGestureListener listenerGesture) {
		this( listenerName, captureBounds, listenerGesture, false);
	}
	
	/**
	 * Create a listener registration data object for a specific gesture
	 * 
	 * @param listenerName
	 *            Name of this subscriber
	 * @param captureBounds
	 *            Bounds on the screen to observe for this gesture to occur
	 * @param listenerGesture
	 *            Gesture listener to to call once this gesture occurs
	 */
	public ListenerRegistration(String listenerName,
			Rectangle2D.Float captureBounds, IGestureListener listenerGesture, boolean genericListener) {
		this.listenerName = listenerName;
		this.captureBounds = captureBounds;
		this.listenerGesture = listenerGesture;
		this.genericListener = genericListener;
	}

	/**
	 * Get the name of this listener
	 * 
	 * @return User-defined name of this listener
	 */
	public String getListenerName() {
		return this.listenerName;
	}

	/**
	 * Set the name of this listener
	 * 
	 * @param value
	 *            Name to set
	 */
	public void setListenerName(String value) {
		this.listenerName = value;
	}

	/**
	 * Get the capture bounds for this gesture
	 * 
	 * @return Capture bound rectangle where the screen looks for this gesture
	 *         to occur
	 */
	public Rectangle2D.Float getCaptureBounds() {
		return this.captureBounds;
	}

	/**
	 * Set the capture bounds for this gesture
	 * 
	 * @param value
	 *            Capture bound rectangle where the screen looks for this
	 *            gesture to occur
	 */
	public void setCaptureBounds(Rectangle2D.Float value) {
		this.captureBounds = value;
	}

	/**
	 * Get the gesture event that will be called when this gesture occurs
	 * 
	 * @return Gesture event that will be called when this gesture occurs
	 */
	public IGestureListener getListenerGesture() {
		return this.listenerGesture;
	}

	/**
	 * Determine if this listener cares about a specific gesture
	 * 
	 * @param g
	 *            Gesture to evaluate
	 * @return True if listening for, false otherwise
	 */
	public boolean caresAboutGesture(Gesture g) {
		if (this.listenerGesture != null) {
			Class<?> gestClass = g.getClass();

			Point2D.Float[] coords = g.getGestureCoords();

			boolean allPointsIn = true;

			if (isListeningFor(gestClass)) {
				int i = 0;
				while (allPointsIn && i < coords.length) {
					allPointsIn = this.captureBounds.contains(coords[i]);
					i++;
				}
				return allPointsIn;
			}
			else {
				//System.out.println( this.getListenerName() + " is not listening for " + gestClass.getCanonicalName() );
			}
		}
		return false;
	}

	private boolean isListeningFor(Class<?> gestClass) {

		//Is this a generic listener?
		if( isGenericListener() ) {
			return true;
		}
		else {
			Class<?>[] listenerInterfaces = this.listenerGesture.getClass()
					.getInterfaces();
			Class<?> listenerInterface = null;
			if (listenerInterfaces.length > 0) {
				listenerInterface = listenerInterfaces[0];
			}
	
			if (GestureEventMap.GESTURE_EVENT_MAP.containsKey(gestClass)) {
				Class<?>[] listeners = GestureEventMap.GESTURE_EVENT_MAP.get(gestClass);
				for (int i = 0; i < listeners.length; i++) {
					if (listeners[i].equals(listenerInterface)) {
						return true;
					}
				}
			}
		}
		
		//Was not a generic listener, and no mapping found for specific gesture listener
		return false;
	}

	/**
	 * Get the gesture type that is registered with this listener
	 * @return Class of gesture type being observed
	 */
	public Class<?> getSubscribedGestureClass() {
		Iterator<?> it = GestureEventMap.GESTURE_EVENT_MAP.keySet().iterator();
		while (it.hasNext()) {
			Class<?> key = (Class<?>) it.next();
			if (isListeningFor(key)) {
				return key;
			}
		}
		return null;
	}
	
	/**
	 * Convert this listener information to XML format
	 * 
	 * @return XML string of this listener name, and gesture information
	 */
	public String toXML() {
		String retXml = "";
		Iterator<?> it = GestureEventMap.GESTURE_EVENT_MAP.keySet().iterator();
		while (it.hasNext()) {
			Class<?> key = (Class<?>) it.next();
			if (isListeningFor(key)) {
				retXml += "<listener listenerName=\"" + this.listenerName
						+ "\" gesture=\"" + key.getName() + "\"/>";
			}
		}
		return retXml;
	}

	public boolean isGenericListener() {
		return genericListener;
	}

	public void setGenericListener(boolean genericListener) {
		this.genericListener = genericListener;
	}
}
